/*****************************************************************************
* Project:  avr-iic-driver
*
* Copyright (C) 2013 Bill Wu. All rights reserved.
*
* This program is open source software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Alternatively, this program may be distributed and modified under the
* terms of Quantum Leaps commercial licenses, which expressly supersede
* the GNU General Public License and are specifically designed for
* licensees interested in retaining the proprietary status of their code.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* e-mail:               wushangru@student.hlju.edu.cn
*****************************************************************************/

#include "../iic/iic_hd.h"
#include "../iic/iic.h"

static void IIC_recordErr(enum IIC_ErrorType error);


static enum IIC_ErrorType g_ErrorType;

static void IIC_recordErr(enum IIC_ErrorType error)
{
    g_ErrorType = error;
}

enum IIC_ErrorType IIC_checkErr(void)
{
    enum IIC_ErrorType temp;

    temp = g_ErrorType;
    g_ErrorType = NOMAL;   /* reset */
    return temp;
}

void IIC_init(enum IIC_Frq frq, enum IIC_IntrPullUp pullUp)
{
    /* SCL frequency = CPU clock frequency / (16 + 2*(TWBR)*4^(TWPS)),
       when TWPS = 0, TWBR = (F_CPU / SCL - 16) / 2                         */
    switch (frq) {
        case IIC_100KHz : {
            TWBR = (uint8_t)((F_CPU / 100000UL - 16) / 2); break;
        }
        case IIC_200KHz : {
            TWBR = (uint8_t)((F_CPU / 200000UL - 16) / 2); break;
        }
        case IIC_400KHz : {
            TWBR = (uint8_t)((F_CPU / 400000UL - 16) / 2); break;
        }
    }

    if (pullUp == PULL_UP) {
        EN_PULL_UP_RESISTOR();
    }

    IIC_recordErr(NOMAL);                           /* reset error variable */
}

uint8_t IIC_writeByte(uint8_t DevAddr, uint8_t IntAddr, uint8_t Data,
                      uint8_t NeedIntAddr)      /* if need internal address */
{
    START();
    WAIT();
    if (TW_STATUS != TW_START) {
        IIC_recordErr(ERR_START);
        return 0;
    }

    WRITE_BYTE(DevAddr|WR);
    WAIT();
    if (TW_STATUS != TW_MT_SLA_ACK) {
        IIC_recordErr(ERR_MT_SLA_ACK);
        return 0;
    }

    if (NeedIntAddr) {
        WRITE_BYTE(IntAddr);
        WAIT();
        if (TW_STATUS != TW_MT_DATA_ACK) {
            IIC_recordErr(TW_MT_DATA_ACK);
            return 0;
        }
    }

    WRITE_BYTE(Data);
    WAIT();
    if (TW_STATUS != TW_MT_DATA_ACK) {
        IIC_recordErr(ERR_MT_DATA_ACK);
        return 0;
    }

    STOP();
    return 1;
}

uint8_t IIC_writeNBytes(uint8_t DevAddr, uint8_t IntAddr, uint8_t* Data,
                        uint8_t NumOfBytes, uint8_t NeedIntAddr)
{
    START();
    WAIT();
    if (TW_STATUS != TW_START) {
        IIC_recordErr(ERR_START);
        return 0;
    }

    WRITE_BYTE(DevAddr|WR);
    WAIT();
    if (TW_STATUS != TW_MT_SLA_ACK) {
        IIC_recordErr(ERR_MT_SLA_ACK);
        return 0;
    }

    if (NeedIntAddr) {
        WRITE_BYTE(IntAddr);
        WAIT();
        if (TW_STATUS != TW_MT_DATA_ACK) {
            IIC_recordErr(TW_MT_DATA_ACK);
            return 0;
        }
    }

    do {
        WRITE_BYTE(*Data++);
        WAIT();
        if (TW_STATUS != TW_MT_DATA_ACK) {
            IIC_recordErr(ERR_MT_DATA_ACK);
            return 0;
        }
    } while (--NumOfBytes);

    STOP();
    return 1;
}


uint8_t IIC_readByte(uint8_t DevAddr, uint8_t IntAddr, uint8_t NeedIntAddr)
{
    uint8_t dat;

    if (NeedIntAddr) {
        START();
        WAIT();
        if (TW_STATUS != TW_START) {
            IIC_recordErr(ERR_START);
            return 1;
        }

        WRITE_BYTE(DevAddr|WR);
        WAIT();
        if (TW_STATUS != TW_MT_SLA_ACK) {
            IIC_recordErr(ERR_MT_SLA_ACK);
            return 1;
        }

        WRITE_BYTE(IntAddr);
        WAIT();
        if (TW_STATUS != TW_MT_DATA_ACK) {
            IIC_recordErr(ERR_MT_DATA_ACK);
            return 1;
        }
    }                                             /* end of if(NeedIntAddr) */

    /* It's START signal when no internal address,but it's a RESTART signal
       when there is internal address                                       */
    START();
    WAIT();
    if ((TW_STATUS != TW_START) && (TW_STATUS != TW_REP_START)) {
        IIC_recordErr(ERR_START);
        return 1;
    }

    WRITE_BYTE(DevAddr|RD);
    WAIT();
    if (TW_STATUS != TW_MR_SLA_ACK) {
        IIC_recordErr(ERR_MR_SLA_ACK);
        return 1;
    }

    TWI_BEGIN();                                               /* read byte */
    WAIT();
    if (TW_STATUS != TW_MR_DATA_NACK) {                             /* NACK */
        IIC_recordErr(ERR_MR_DATA_NACK);
        return 1;
    }
    dat = TWDR;

    STOP();
    return dat;
}

uint16_t IIC_read2Bytes(uint8_t DevAddr, uint8_t IntAddr,/*internal address */
                        uint8_t NeedIntAddr)    /* if need internal address */
{
    uint8_t HighByte;
    uint16_t Data;

    if (NeedIntAddr) {
        START();
        WAIT();
        if (TW_STATUS != TW_START) {
            IIC_recordErr(ERR_START);
            return 1;
        }

        WRITE_BYTE(DevAddr|WR);
        WAIT();
        if (TW_STATUS != TW_MT_SLA_ACK) {
            IIC_recordErr(ERR_MT_SLA_ACK);
            return 1;
        }

        WRITE_BYTE(IntAddr);
        WAIT();
        if (TW_STATUS != TW_MT_DATA_ACK) {
            IIC_recordErr(ERR_MT_DATA_ACK);
            return 1;
        }
    }                                             /* end of if(NeedIntAddr) */

    /* It's START signal when no internal address,but it's a RESTART signal
       when there is internal address                                       */
    START();
    WAIT();
    if ((TW_STATUS != TW_START) && (TW_STATUS != TW_REP_START)) {
        IIC_recordErr(ERR_START);
        return 1;
    }

    WRITE_BYTE(DevAddr|RD);
    WAIT();
    if (TW_STATUS != TW_MR_SLA_ACK) {
        IIC_recordErr(ERR_MR_SLA_ACK);
        return 1;
    }

    TWI_BEGIN();                                         /* read first byte */
    ACK();       /* very important:generate an ACK signal when receive data */
    WAIT();
    if (TW_STATUS != TW_MR_DATA_ACK) {
        IIC_recordErr(ERR_MR_DATA_ACK);
        return 1;
    }
    HighByte = TWDR;

    TWI_BEGIN();                                        /* read second byte */
    WAIT();
    if (TW_STATUS != TW_MR_DATA_NACK) {
        IIC_recordErr(ERR_MR_DATA_NACK);
        return 1;
    }
    Data = (HighByte << 8) + TWDR;

    STOP();
    return Data;
}

uint8_t IIC_readNByte(uint8_t DevAddr, uint8_t IntAddr, uint8_t* Data,
                      uint8_t DataLen, uint8_t NeedIntAddr)
{
    if (NeedIntAddr) {
        START();
        WAIT();
        if (TW_STATUS != TW_START) {
            IIC_recordErr(ERR_START);
            return 1;
        }

        WRITE_BYTE(DevAddr|WR);
        WAIT();
        if (TW_STATUS != TW_MT_SLA_ACK) {
            IIC_recordErr(ERR_MT_SLA_ACK);
            return 1;
        }

        WRITE_BYTE(IntAddr);
        WAIT();
        if (TW_STATUS != TW_MT_DATA_ACK) {
            IIC_recordErr(ERR_MT_DATA_ACK);
            return 1;
        }
    }                                             /* end of if(NeedIntAddr) */

    /* It's START signal when no internal address,but it's a RESTART signal
       when there is internal address                                       */
    START();
    WAIT();
    if ((TW_STATUS != TW_START) && (TW_STATUS != TW_REP_START)) {
        IIC_recordErr(ERR_START);
        return 1;
    }

    WRITE_BYTE(DevAddr|RD);
    WAIT();
    if (TW_STATUS != TW_MR_SLA_ACK) {
        IIC_recordErr(ERR_MR_SLA_ACK);
        return 1;
    }

    DataLen -= 1;                                   /* read DataLen-1 bytes */
    do {
        TWI_BEGIN();
        ACK();   /* very important:generate an ACK signal when receive data */
        WAIT();
        if (TW_STATUS != TW_MR_DATA_ACK) {
            IIC_recordErr(ERR_MR_DATA_ACK);
            return 1;
        }
        *Data++ = TWDR;
    } while (--DataLen);

    TWI_BEGIN();                             /* read last byte, return NACK */
    WAIT();
    if (TW_STATUS != TW_MR_DATA_NACK) {
        IIC_recordErr(ERR_MR_DATA_NACK);
        return 1;
    }
    *Data = TWDR;

    STOP();
    return 0;
}










